﻿using System.Text;
using CppAst;

namespace ast;

public static class GoTypeUtil
{
    public static string GetGoRefTypeString(CppReferenceType cppPointerType)
    {
        return GetGoPointerTypeString(new CppPointerType(cppPointerType.ElementType));
    }

    public static string GetGoPointerTypeString(CppPointerType cppPointerType)
    {
        if (ClassUtil.IsVoidAnyPtr(cppPointerType))
        {
            return "uintptr";
        }
        if (cppPointerType.ElementType is CppArrayType cppArrayType)
        {
            return GetGoTypeString(cppArrayType);
        }

        if (cppPointerType.ElementType is CppClass cppClass)
        {
            return GetGoTypeString(cppPointerType.ElementType);
        }

        return "*" + GetGoTypeString(cppPointerType.ElementType);
    }

    public static string GetGoArrayTypeString(CppArrayType cppArrayType, bool parentIsArray)
    {
        var elementString = "";
        if (cppArrayType.ElementType is CppClass)
        {
            // if (cppArrayType.Size == -1)
            // {
            return $"{GetGoTypeString(cppArrayType.ElementType, true)}";
            // }
            // else
            // {
            // elementString = $"[{cppArrayType.Size}]{GetGoTypeString(cppArrayType.ElementType, true)}";
            // }
        }

        if (cppArrayType.Size == -1)
        {
            elementString = $"{GetGoTypeString(cppArrayType.ElementType, true)}";
        }
        else
        {
            elementString = $"[{cppArrayType.Size}]{GetGoTypeString(cppArrayType.ElementType, true)}";
        }

        if (!parentIsArray)
        {
            elementString = "*" + elementString;
        }

        return elementString;
    }


    public static string GetGoTypeString(CppType cppType, bool parentIsArray = false)
    {
        if (ClassUtil.IsVoidAnyPtr(cppType))
        {
            return "uintptr";
        }

        switch (cppType)
        {
            case CppPointerType cppPointerType:
                return GetGoPointerTypeString(cppPointerType);
            case CppReferenceType cppReferenceType:
                return GetGoRefTypeString(cppReferenceType);
            case CppPrimitiveType cppPrimitiveType:
                switch (cppPrimitiveType.Kind)
                {
                    case CppPrimitiveKind.Void:
                        return "";
                    case CppPrimitiveKind.Bool:
                        return "bool";
                    case CppPrimitiveKind.WChar:
                        return "int16";
                    case CppPrimitiveKind.SignedChar:
                        return "int8";
                    case CppPrimitiveKind.Char:
                        return "int8";
                    case CppPrimitiveKind.Short:
                        return "int16";
                    case CppPrimitiveKind.Int:
                        return "int32";
                    case CppPrimitiveKind.Long:
                        return "int";
                    case CppPrimitiveKind.LongLong:
                        return "int64";
                    case CppPrimitiveKind.UnsignedChar:
                        return "uint8";
                    case CppPrimitiveKind.UnsignedShort:
                        return "uint16";
                    case CppPrimitiveKind.UnsignedInt:
                        return "uint32";

                    case CppPrimitiveKind.UnsignedLongLong:
                        return "uint64";
                    case CppPrimitiveKind.Float:
                        return "float32";
                    case CppPrimitiveKind.Double:
                        return "float64";
                    case CppPrimitiveKind.LongDouble:
                        return "float64";
                    default:
                        throw new Exception("primitive type not registered");
                }
            case CppEnum cppEnumType:
                //todo 添加枚举到go文件中
                return GetGoEnumName(cppEnumType);
            case CppClass cppClassType:
                return GetGoClassName(cppClassType);
            case CppArrayType cppArrayType:
                return GetGoArrayTypeString(cppArrayType, parentIsArray);
            case CppUnexposedType unexposedType:
                if (unexposedType.Name.Equals("__attribute__((__vector_size__(4 * sizeof(float)))) float"))
                    return "[4]float32";

                break;
        }

        throw new Exception("go type not registered");
    }

    public static string GetGoClassName(CppClass cppClass)
    {
        var goClassName = ClassUtil.FirstToUpper(cppClass.FullName);
        if (cppClass.TemplateKind == CppTemplateKind.TemplateSpecializedClass)
        {
            StringBuilder sb = new StringBuilder();
            string fullparent = cppClass.FullParentName;
            var useName = cppClass.Name;

            if (string.IsNullOrEmpty(fullparent))
            {
                sb.Append(useName);
            }
            else
            {
                sb.Append($"{fullparent}::{useName}");
            }


            for (int i = 0; i < cppClass.TemplateSpecializedArguments.Count; i++)
            {
                var tp = cppClass.TemplateSpecializedArguments[i];
                if (tp.ArgAsType != null)
                {
                    sb.Append(ClassUtil.FirstToUpper(tp.ArgAsType.FullName));
                }
                else if (tp.ArgString != null)
                {
                    sb.Append(ClassUtil.FirstToUpper(tp.ArgString));
                }
                else if (tp.ArgAsInteger != null)
                {
                    sb.Append(tp.ArgAsInteger);
                }
                else
                {
                    sb.Append(tp.ArgAsUnknown);
                }
            }

            goClassName = sb.ToString();
            goClassName = ClassUtil.FirstToUpper(goClassName);
        }

        var implName = goClassName + "Ptr";
        implName = implName.Replace("::", "");
        implName = implName.Replace(" ", "");
        implName = implName.Replace("<", "");
        implName = implName.Replace(">", "");
        implName = implName.Replace("*", "Ptr");
        implName = implName.Replace("&", "Ref");
        implName = implName.Replace("-", "");
        return implName;
    }

    public static string GetGoEnumName(CppEnum cppEnum)
    {
        var goClassName = ClassUtil.FirstToUpper(cppEnum.FullName);
        var implName = "Enum" + goClassName;
        implName = implName.Replace("::", "");
        // implName = implName.Replace("_", "");
        return implName;
    }

    public static string GetGoFunctionName(FunctionInfo functionInfo, int index)
    {
        var functionInfoName = functionInfo.Function.Name;
        var goFunctionName = ClassUtil.FirstToUpper(functionInfoName);
        if (functionInfo.Function.IsConstructor)
        {
            goFunctionName = "New" + goFunctionName;
        }

        if (index != 0)
        {
            goFunctionName += index + 10;
        }

        goFunctionName = goFunctionName.Replace("::", "_");
        goFunctionName = goFunctionName.Replace("<", "_");
        goFunctionName = goFunctionName.Replace(">", "");
        goFunctionName = goFunctionName.Replace(",", "_");
        goFunctionName = goFunctionName.Replace(" ", "");
        goFunctionName = goFunctionName.Replace("*", "Ptr");
        goFunctionName = goFunctionName.Replace("&", "Ref");
        goFunctionName = goFunctionName.Replace("-", "");
        return goFunctionName;
    }
}